using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using Common.Shared.Reflection;
using Common.Shared.Design;
using System.Reflection;
using System.Collections;

namespace Common.Shared.Json.Serialization {
	public class JsonSerializer<T> {
		int tabDepth = 0;
		public void Serialize ( Stream stream, T o, bool compressed ) {
			if ( o == null ) {
				return;
			}
			using ( StreamWriter sw = new StreamWriter ( stream ) ) {
				WriteValue ( sw, o, null, true, compressed );
			}
		}

		public void Serialize ( Stream stream, T o ) {
			Serialize ( stream, o, true );
		}

		public void Serialize ( FileInfo file, T o ) {
			using ( FileStream fs = new FileStream ( file.FullName, FileMode.Create, FileAccess.Write ) ) {
				Serialize ( fs, o );
			}
		}

		public void Serialize ( FileInfo file, T o, bool compressed ) {
			using ( FileStream fs = new FileStream ( file.FullName, FileMode.Create, FileAccess.Write ) ) {
				Serialize ( fs, o, compressed );
			}
		}
		private void WriteValue ( StreamWriter sw, object objValue, object parentObject, bool noclose, bool compressed ) {
			Type objType = null;
			if ( objValue != null ) {
				objType = objValue.GetType ( );
			}
			if ( objValue == null || objType == null ) {
				FormattedWrite ( sw, 0, "null", compressed, false );
				if ( !noclose ) {
					sw.Write ( ";{0}", !compressed ? Environment.NewLine : string.Empty );
				}
				return;
			}


			JsonObjectAttribute joa = AttributeUtils.GetAttribute<JsonObjectAttribute> ( objType );

			if ( joa != null && !string.IsNullOrEmpty ( joa.Name ) ) {
				FormattedWrite ( sw, tabDepth, string.Format ( "\"{0}\":", joa.Name ), compressed, false );
			} else if ( objType.IsSubclassOf ( typeof ( PropertyInfo ) ) || objType.IsSubclassOf ( typeof ( FieldInfo ) ) ) {
				FormattedWrite ( sw, tabDepth, string.Format ( "\"{0}\":", ( objValue as MemberInfo ).Name.ToLower ( ) ), compressed, false );
			}

			if ( TypeUtils.IsString ( objType ) ) {
				FormattedWrite ( sw, 0, string.Format ( "\"{0}\"", (string)objValue ), compressed, false );
			} else if ( TypeUtils.IsNumeric ( objType ) ) {
				FormattedWrite ( sw, 0, objValue.ToString ( ), compressed, false );
			} else if ( objValue is DateTime ) {
				FormattedWrite ( sw, 0, string.Format ( "\\/Date({0})\\/", DateTimeConverter.ToUnixEpoch ( ( (DateTime)objValue ) ) ), compressed, false );
			} else if ( objValue is Boolean ) {
				FormattedWrite ( sw, 0, objValue.ToString ( ).ToLower ( ), compressed, false );
			} else if ( objValue is Guid ) {
				FormattedWrite ( sw, 0, string.Format ( "\"{{{0}}}\"", ( (Guid)objValue ).ToString ( "D" ) ), compressed, false );
			} else if ( TypeUtils.IsCollection ( objType ) ) {
				FormattedWrite ( sw, 0, "[", compressed, true );
				tabDepth++;
				int count = 0;
				int itemCount = objType == typeof ( Array ) ? ( objValue as Array ).Length : ( objValue as IList ).Count;
				foreach ( object var in ( objValue as IEnumerable ) ) {
					if ( !compressed ) {
						sw.Write ( GetTabDepthString ( tabDepth ) );
					}
					WriteValue ( sw, var, objValue, true, compressed );
					if ( ++count < itemCount ) {
						FormattedWrite ( sw, 0, ",", compressed, true );
					} else {
						FormattedWrite ( sw, 0, string.Empty, compressed, true );
					}
				}
				FormattedWrite ( sw, --tabDepth, "]", compressed, false );
			} else if ( objType.IsSubclassOf ( typeof ( PropertyInfo ) ) || objType.IsSubclassOf ( typeof ( FieldInfo ) ) ) {
				object value = MemberUtils.GetMemberInfoValue<object> ( parentObject, objValue as MemberInfo );
				WriteValue ( sw, value, parentObject, false, compressed );
			} else {
				FormattedWrite ( sw, 0, "{", compressed, true );
				tabDepth++;
				if ( objType != typeof ( JsonTypeInfo ) ) {
					WriteValue ( sw, new JsonTypeInfo ( objType ), objValue, false, compressed );
				}

				foreach ( PropertyInfo pi in MemberUtils.GetProperties ( objValue ) ) {
					WriteValue ( sw, pi, objValue, true, compressed );
				}

				foreach ( FieldInfo fi in MemberUtils.GetFields ( objValue ) ) {
					WriteValue ( sw, fi, objValue, true, compressed );
				}
				FormattedWrite ( sw, --tabDepth, "}", compressed, false );
			}
			if ( !noclose ) {
				sw.Write ( ";{0}", !compressed ? Environment.NewLine : string.Empty );
			}

		}

		private void FormattedWrite ( StreamWriter sw, int tabDepth, string val, bool compressed, bool newLine ) {
			if ( !compressed ) {
				sw.Write ( "{0}{1}{2}", GetTabDepthString(tabDepth), val, newLine ? Environment.NewLine : string.Empty );
			} else {
				sw.Write ( "{0}", val);
			}
		}

		private string GetTabDepthString ( int tabDepth ) {
			StringBuilder tabs = new StringBuilder ( );
			for ( int i = 0; i < tabDepth; i++ ) {
				tabs.Append ( "\t" );
			}
			return tabs.ToString ( );
		}

		private void InternalSerializeMemberInfo ( Stream stream, object o, MemberInfo mi, bool compressed ) {
			AttributeUtils.GetAttribute<JsonObjectAttribute> ( mi );
		}


	}
}
